import os
from exceptions import NotImplementedException
from utils import *

class DictSerializableModel(object):
    
    def dump(self):
        raise NotImplementedException()
        
    def load(self):
        raise NotImplementedException()

class MirrorModel(DictSerializableModel):
    
    def __init__(self, sets=None):
        if sets is not None:
            #should only be none when serializing
            self.sets = sets
            self.last_updated = 0
            self.filtered = []
       
    def dump(self):
        return {
            'sets' : [],
            'last_updated' : self.last_updated,
            'filtered' : self.filtered
        }
        
    def load(self, value):
        self.sets = value['sets']
        self.last_updated = value['last_updated']
        self.filtered = value['filtered']
        return self
        
    def filtered_sets(self):
        sets = []
        
        for s in self.sets:
            if s.id not in self.filtered:
                sets.append(s)
                
        return sets
            
    def has_set(self, id):
        
        for set in self.sets:
            if set.id == id or id == set.title:
                return True
                
        return False

    def get_set(self, id):
        
        for set in self.sets:
            if set.id == id or id == set.title:
                return set
                
        return False
        

class Node(object):
    
    def __init__(self, node):
        self.node = node

    def get_node(self):
        return None
    def set_node(self, node):
        self.set_id(node)
        self.set_title(node)
    
    node = property(get_node, set_node)
    
    def set_id(self, node):
        self.id = node.get('id')
        
    def set_title(self, node):
        raise NotImplementedException("Must implement set_title on object")

class Set(Node, DictSerializableModel):
    
    def __init__(self, node=None):
        if node is not None:
            #should only be none when serializing
            self.node = node
            self.photos = []
            self.dirty = True
            self.file_saved_location = ''
        
    def dump(self):
        return {
            'photos' : [photo.dump() for photo in self.photos],
            'dirty' : self.dirty,
            'id' : self.id,
            'title' : self.title,
            'num_photos' : self.num_photos,
            'num_videos' : self.num_videos,
            'file_saved_location' : self.file_saved_location
        }
    
    def load(self, value):
        self.photos = [Photo().load(p, self) for p in value['photos']]
        self.dirty = value['dirty']
        self.id = value['id']
        self.title = value['title']
        self.num_photos = value['num_photos']
        self.num_videos = value['num_videos']
        self.file_saved_location = value['file_saved_location']
        return self
        
    def delete(self):
        logger.info("Deleting set %s" % self.id)
        for photo in self.photos:
            photo.delete()
            
        if os.path.exists(self.file_saved_location):
            logger.info("removing set settings at %s" % (self.title, self.file_saved_location))
            os.remove(self.file_saved_location)
            
        del self
        
    def update(self, otherset):
        if self.title != otherset.title:
            self.title = otherset.title
            self.dirty = True
            
        if self.num_photos != otherset.num_photos:
            self.num_photos = otherset.num_photos
            self.dirty = True
            
        if self.num_videos != otherset.num_videos:
            self.num_videos = otherset.num_videos
            self.dirty = True
        
    def append_photo(self, photo):
        self.photos.append(photo)
        self.dirty = True
        
    def remove_photo(self, photo):
        self.photos.remove(photo)
        self.dirty = True
        
    def get_photo(self, id):
        
        for photo in self.photos:
            if photo.id == id:
                return photo
                
        return False
        
    def has_photo(self, id):
        
        for photo in self.photos:
            if photo.id == id:
                return True
                
        return False
        
    def add_photos(self, set_node):
        for photo in set_node.getchildren():
            self.append_photo(Photo(set_node, photo, self))
        
        self.dirty = True
    
    def get_node(self):
        return None
    def set_node(self, node):
        self.dirty = True
        Node.set_node(self, node)
        self.set_number_of_photos(node)
        self.set_number_of_videos(node)
    
    node = property(get_node, set_node)
    
    def set_title(self, node):
        self.title = node.find('title').text
    
    def set_number_of_photos(self, node):
        self.num_photos = int(node.get('photos'))
    
    def set_number_of_videos(self, node):
        self.num_videos = int(node.get('videos'))
        
    def get_number_of_photos_and_videos(self):
        return len(self.photos)

    number_of_photos_and_videos = property(get_number_of_photos_and_videos)
        
    def __str__(self):
        return "<flickr Set>%s, id: %s, photos: %i</flickr Set>" % (
            hasattr(self, 'title') and self.title or '',
            hasattr(self, 'id') and self.id or '',
            hasattr(self, 'number_of_photos_and_videos') and self.number_of_photos_and_videos or 0
        )
    
class Photo(Node, DictSerializableModel):
    
    def __init__(self, set_node=None, photo_node=None, parent=None):
        if set_node is not None and photo_node is not None and parent is not None:
            # should only be none when serializing
            self.set = parent
            self.set_info(set_node, photo_node)
            self.location = ''
            self.dirty = True
    
    def dump(self):
        return {
            'owner' : self.owner,
            'title' : self.title,
            'id' : self.id,
            'secret' : self.secret,
            'media' : self.media,
            'dirty' : self.dirty,
            'originalsecret' : self.originalsecret,
            'farm' : self.farm,
            'server' : self.server,
            'originalformat' : self.originalformat,
            'lastupdate' : self.lastupdate,
            'location' : self.location
        }
        
    def load(self, value, parent):
        self.set = parent
        
        for k, v in value.items():
            setattr(self, k, v)
            
        return self
    
    def delete(self):
        self.set.dirty = True
        if os.path.exists(self.location):
            os.remove(self.location)
    
        del self
    
    def get_dirty(self):
        if not os.path.exists(self.location):
            return True
        else:
            return self._dirty
        
    def set_dirty(self, value):
        self._dirty = value
        self.set.dirty = True
        
    dirty = property(get_dirty, set_dirty)
    
    def set_info(self, set_node, photo_node):
        self.set.dirty = True
        Node.set_node(self, photo_node)
        self.set_secret(photo_node)
        self.set_originalsecret(photo_node)
        self.set_farm(photo_node)
        self.set_originalformat(photo_node)
        self.set_server(photo_node)
        self.set_lastupdate(photo_node)
        self.set_media(photo_node)
        self.set_owner(set_node)
    
    def set_owner(self, node):
        self.owner = node.get('owner')
    
    def set_title(self, node):
        self.title = node.get('title')
    
    def set_secret(self, node):
        self.secret = node.get('secret')
    
    def set_media(self, node):
        self.media = node.get('media')
    
    def set_originalsecret(self, node):
        self.originalsecret = node.get('originalsecret')
    
    def set_farm(self, node):
        self.farm = node.get('farm')
    
    def set_server(self, node):
        self.server = node.get('server')
    
    def set_originalformat(self, node):
        self.originalformat = node.get('originalformat')
    
    def set_lastupdate(self, node):
        self.lastupdate = int(node.get('lastupdate'))
    
    def get_filename(self):
        if self.media == 'video':
            return "%s-%s.avi" % (self.id, normalize(self.title))
        else:
            return "%s-%s.%s" % (self.id, normalize(self.title), self.originalformat)
         
    filename = property(get_filename)
            
    def __str__(self):
        return "<flickr Photo>%s, in set %s</flickr Photo>" % (
            hasattr(self, 'title') and self.title or '',
            hasattr(self, 'set') and self.set or ''
        )